package org.dromara.system.service.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import org.dromara.common.core.constant.CacheNames;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.dromara.common.redis.utils.CacheUtils;
import org.dromara.common.redis.utils.RedisUtils;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.constants.OrderConstants;
import org.dromara.system.domain.PayAccount;
import org.dromara.system.domain.PayGroup;
import org.dromara.system.domain.PayGroupAccount;
import org.dromara.system.domain.PayGroupType;
import org.dromara.system.domain.bo.PayGroupAccountAddBo;
import org.dromara.system.domain.vo.PayPolicyVo;
import org.dromara.system.kernel.OrderChartKernel;
import org.dromara.system.mapper.*;
import org.dromara.system.service.IPayAccountService;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.dromara.system.domain.bo.PayGroupAccountBo;
import org.dromara.system.domain.vo.PayGroupAccountVo;
import org.dromara.system.service.IPayGroupAccountService;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 轮询组收款账号Service业务层处理
 *
 * @author Gavino
 * @date 2024-10-22
 */
@RequiredArgsConstructor
@Service
public class PayGroupAccountServiceImpl implements IPayGroupAccountService {

    private final PayGroupAccountMapper baseMapper;
    private final PayGroupMapper payGroupMapper;
    private final PayGroupTypeMapper payGroupTypeMapper;
    private final PayAccountMapper payAccountMapper;
    private final IPayAccountService payAccountService;


    /**
     * 查询轮询组收款账号
     *
     * @param id 主键
     * @return 轮询组收款账号
     */
    @Override
    public PayGroupAccount queryById(Long id) {
        PayGroupAccount obj = RedisUtils.getCacheObject(OrderConstants.pay_group_account + id);
        if(obj == null) {
            obj = baseMapper.selectById(id);
            if(obj != null) {
                RedisUtils.setCacheObject(OrderConstants.pay_group_account + id, obj);
            }
        }
        return obj;
    }

    /**
     * 分页查询轮询组收款账号列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 轮询组收款账号分页列表
     */
    @Override
    public TableDataInfo<PayGroupAccountVo> queryPageList(PayGroupAccountBo bo, PageQuery pageQuery) {
        MPJLambdaWrapper<PayGroupAccount> lqw = buildQueryWrapper(bo);
        lqw.leftJoin(PayAccount.class, PayAccount::getAccountId, PayGroupAccount::getAccountId);
        lqw.selectAll(PayGroupAccount.class);
        lqw.select(PayAccount::getAccountName);
        lqw.orderByDesc(PayGroupAccount::getSort);
        Page<PayGroupAccountVo> result = baseMapper.selectJoinPage(pageQuery.build(), PayGroupAccountVo.class, lqw);

        List<PayGroupAccountVo> list = result.getRecords();
        list = list.stream().peek(item -> {
            if ((item.getStatus() == 1 || item.getStatus() == 2) && item.getAutoUp() == 1 && item.getTotalLimit() != null && item.getTotalLimit().compareTo(BigDecimal.ZERO) > 0) {
                item.setLimitMoney(item.getTotalLimit().subtract(item.getTotalMoney()));
            }
        }).toList();

        result.setRecords(list);

        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的轮询组收款账号列表
     *
     * @param bo 查询条件
     * @return 轮询组收款账号列表
     */
    @Override
    public List<PayGroupAccountVo> queryList(PayGroupAccountBo bo) {
        MPJLambdaWrapper<PayGroupAccount> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private MPJLambdaWrapper<PayGroupAccount> buildQueryWrapper(PayGroupAccountBo bo) {
        Map<String, Object> params = bo.getParams();
        MPJLambdaWrapper<PayGroupAccount> lqw = new MPJLambdaWrapper<>();
        if (LoginHelper.isAgent()) {
            lqw.eq(PayGroupAccount::getAgentId, LoginHelper.getUserId());
        }
        lqw.eq(bo.getGroupId() != null, PayGroupAccount::getGroupId, bo.getGroupId());
        lqw.eq(bo.getGroupTypeId() != null, PayGroupAccount::getGroupTypeId, bo.getGroupTypeId());
        lqw.eq(bo.getPayTypeId() != null, PayGroupAccount::getPayTypeId, bo.getPayTypeId());
        lqw.eq(bo.getAccountId() != null, PayGroupAccount::getAccountId, bo.getAccountId());
        lqw.eq(bo.getStatus() != null, PayGroupAccount::getStatus, bo.getStatus());
        lqw.eq(bo.getTotalLimit() != null, PayGroupAccount::getTotalLimit, bo.getTotalLimit());
        lqw.eq(bo.getIncreaseRate() != null, PayGroupAccount::getIncreaseRate, bo.getIncreaseRate());
        lqw.eq(StringUtils.isNotBlank(bo.getRefreshTime()), PayGroupAccount::getRefreshTime, bo.getRefreshTime());
        lqw.eq(bo.getMinMoney() != null, PayGroupAccount::getMinMoney, bo.getMinMoney());
        lqw.eq(bo.getMaxMoney() != null, PayGroupAccount::getMaxMoney, bo.getMaxMoney());
        lqw.eq(bo.getTotalMoney() != null, PayGroupAccount::getTotalMoney, bo.getTotalMoney());
        lqw.eq(bo.getPayMethod() != null, PayGroupAccount::getPayMethod, bo.getPayMethod());
        lqw.eq(StringUtils.isNotBlank(bo.getReceiveList()), PayGroupAccount::getReceiveList, bo.getReceiveList());
        lqw.eq(bo.getFailLimit() != null, PayGroupAccount::getFailLimit, bo.getFailLimit());
        lqw.eq(bo.getSort() != null, PayGroupAccount::getSort, bo.getSort());
        lqw.eq(bo.getAgentId() != null, PayGroupAccount::getAgentId, bo.getAgentId());
        return lqw;
    }

    /**
     * 新增轮询组收款账号
     *
     * @param bo 轮询组收款账号
     * @return 是否新增成功
     */
    @Transactional
    @Override
    public R<?> insertBatch(PayGroupAccountAddBo bo) {
        if (bo.getGroupId() == null) {
            return R.fail("轮询组ID不能为空");
        }
        if (bo.getAccountIds() == null || bo.getAccountIds().isEmpty()) {
            return R.fail("请选择收款账号");
        }

        //查询轮询组信息
        PayGroup group = payGroupMapper.selectById(bo.getGroupId());
        if (group == null) {
            return R.fail("未查询到轮询组");
        }

        PayGroupType groupType = payGroupTypeMapper.selectById(bo.getGroupTypeId());
        if (groupType == null || !Objects.equals(groupType.getGroupId(), bo.getGroupId())) {
            return R.fail("该轮询组下未添加此支付平台");
        }

        //查询账号信息
        List<PayAccount> account = payAccountMapper.selectBatchIds(bo.getAccountIds());
        if (account == null) {
            return R.fail("所选收款账号无效");
        }
        String temps = account.stream().filter(tmp -> Objects.equals(tmp.getStatus(), 2)).map(PayAccount::getAccountName).collect(Collectors.joining("、"));
        if (StringUtils.isNotEmpty(temps)) {
            return R.fail("以下账户已封禁：" + temps + "，请刷新重试");
        }

        List<PayGroupAccount> hasList = baseMapper.selectList(new LambdaQueryWrapper<PayGroupAccount>().eq(PayGroupAccount::getGroupId, bo.getGroupId()).eq(PayGroupAccount::getGroupTypeId, bo.getGroupTypeId()).in(PayGroupAccount::getAccountId, bo.getAccountIds()));
        if (!hasList.isEmpty()) {
            List<Long> hasIds = hasList.stream().map(PayGroupAccount::getAccountId).toList();
            account = account.stream().filter(tmp -> !hasIds.contains(tmp.getAccountId())).toList();
        }

        if (account.isEmpty()) {
            return R.fail("所选账号已添加到账号列表");
        }

        List<PayGroupAccount> adds = new ArrayList<>();
        account.forEach(act -> {
            PayGroupAccount obj = new PayGroupAccount();
            obj.setGroupId(group.getGroupId());
            obj.setGroupTypeId(bo.getGroupTypeId());
            obj.setAccountId(act.getAccountId());
            obj.setPayTypeId(act.getPayTypeId());
            obj.setApiType(act.getApiType());
            obj.setAgentId(group.getAgentId());
            obj.setAgentName(group.getAgentName());
            obj.setStatus(2);
            adds.add(obj);
        });
        baseMapper.insertBatch(adds);

        return R.ok();
    }

    /**
     * 新增轮询组收款账号
     *
     * @param bo 轮询组收款账号
     * @return 是否新增成功
     */
    @Transactional
    @Override
    public R<?> insertByBo(PayGroupAccountBo bo) {
        if (bo.getGroupId() == null) {
            return R.fail("轮询组ID不能为空");
        }
        if (bo.getAccountId() == null) {
            return R.fail("请选择收款账号");
        }

        //查询轮询组信息
        PayGroup group = payGroupMapper.selectById(bo.getGroupId());
        if (group == null) {
            return R.fail("未查询到轮询组");
        }

        //查询账号信息
        PayAccount account = payAccountService.selectById(bo.getAccountId());
        if (account == null) {
            return R.fail("所选收款账号无效");
        }
        if (account.getStatus() == 2) {
            return R.fail("收款账户被封禁，不能添加到此轮询组");
        }

        if (baseMapper.selectCount(new LambdaQueryWrapper<PayGroupAccount>().eq(PayGroupAccount::getGroupId, bo.getGroupId()).eq(PayGroupAccount::getAccountId, account.getAccountId())) > 0) {
            return R.fail("已添加【" + account.getAccountName() + "】,请问重复此操作");
        }

        PayGroupAccount add = MapstructUtils.convert(bo, PayGroupAccount.class);

        add.setApiType(account.getApiType());
        add.setPayTypeId(account.getPayTypeId());
        add.setAgentId(group.getAgentId());
        add.setAgentName(group.getAgentName());

        if (add.getTotalLimit() == null || add.getTotalLimit().compareTo(BigDecimal.ZERO) <= 0) {
            add.setTotalLimit(BigDecimal.ZERO);
            add.setIncreaseRate(0);
            add.setRefreshTime("");
            add.setAutoUp(0);
        }

        baseMapper.insert(add);


        return R.ok();
    }

    /**
     * 修改轮询组收款账号
     *
     * @param bo 轮询组收款账号
     * @return 是否修改成功
     */
    @Override
    public R<?> updateByBo(PayGroupAccountBo bo) {
        PayGroupAccount update = MapstructUtils.convert(bo, PayGroupAccount.class);
        if (update == null) {
            return R.fail("当前数据不存在");
        }

        PayGroupAccount old = baseMapper.selectById(bo.getId());
        if (old == null) {
            return R.fail("当前数据不存在");
        }

        //查询账号信息
        if (old.getAccountId() != null) {
            PayAccount account = payAccountService.selectById(old.getAccountId());
            if (account == null || Objects.equals(account.getIsDelete(), 1)) {
                return R.fail("对应收款账号无效或已删除");
            }

            if (account.getStatus() == 2 && (Objects.equals(update.getStatus(), 1) || Objects.equals(update.getStatus(), 2))) {
                return R.fail("收款账号【" + account.getAccountName() + "】已封禁，不能修改状态");
            }

            update.setApiType(account.getApiType());
            update.setPayTypeId(account.getPayTypeId());
        }

        update.setUpdateId(LoginHelper.getUserId());
        update.setUpdateName(LoginHelper.getUsername());
        update.setUpdateTime(DateUtils.getNowDate());

        if (update.getTotalLimit() == null || update.getTotalLimit().compareTo(BigDecimal.ZERO) <= 0) {
            update.setTotalLimit(BigDecimal.ZERO);
            update.setIncreaseRate(0);
            update.setRefreshTime("");
            update.setAutoUp(0);
        }

        if ((old.getTotalLimit() == null || old.getTotalLimit().compareTo(BigDecimal.ZERO) == 0) && update.getTotalLimit().compareTo(BigDecimal.ZERO) > 0) {
            update.setTotalMoney(BigDecimal.ZERO);
        }

        RedisUtils.deleteObject(OrderConstants.pay_group_account + bo.getId());
        return baseMapper.updateById(update) > 0 ? R.ok() : R.fail();
    }

    /**
     * 修改轮询组收款账号
     *
     * @param account 轮询组收款账号
     * @return 是否修改成功
     */
    @Override
    public Boolean updateById(PayGroupAccount account) {
        RedisUtils.deleteObject(OrderConstants.pay_group_account + account.getId());
        return baseMapper.updateById(account) > 0;
    }

    /**
     * 修改轮询组收款账号
     *
     * @param account 轮询组收款账号
     * @return 是否修改成功
     */
    @Override
    public Boolean update(PayGroupAccount account, Wrapper<PayGroupAccount> wrapper) {
        List<PayGroupAccount> list = baseMapper.selectList(wrapper);
        for (PayGroupAccount groupAccount : list) {
            RedisUtils.deleteObject(OrderConstants.pay_group_account + groupAccount.getId());
        }
        return baseMapper.update(account, wrapper) > 0;
    }


    /**
     * 校验并批量删除轮询组收款账号信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(ids.isEmpty()) {
            return true;
        }

        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        LambdaQueryWrapper<PayGroupAccount> query = new LambdaQueryWrapper<>();
        query.in(PayGroupAccount::getId, ids);
        if (LoginHelper.isAgent()) {
            query.eq(PayGroupAccount::getAgentId, LoginHelper.getUserId());
        }
        for (Long id : ids) {
            RedisUtils.deleteObject(OrderConstants.pay_group_account + id);
        }
        return baseMapper.delete(query) > 0;
    }

    /**
     * 根据条件删除
     *
     * @param queryWrapper 条件
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteByQuery(Wrapper<PayGroupAccount> queryWrapper){
        List<PayGroupAccount> list = baseMapper.selectList(queryWrapper);
       return deleteWithValidByIds(list.stream().map(PayGroupAccount::getId).collect(Collectors.toSet()), false);
    }

    /**
     * 刷新收款账号收款金额、数量限制
     *
     * @return 结果
     */
    @Override
    public void refreshAccountMoney() {
        LocalDateTime time = LocalDateTime.now();
        int hour = time.getHour();
        int minute = time.getMinute();

        String minuteStr = minute < 30 ? "00" : "30";
        String hourStr = String.format("%02d", hour) + ":" + minuteStr;

        List<PayGroupAccount> accounts = baseMapper.selectList(new LambdaQueryWrapper<PayGroupAccount>()
            .eq(PayGroupAccount::getRefreshTime, hourStr)
            .in(PayGroupAccount::getStatus, 1, 2)
        );

        if (!accounts.isEmpty()) {
            baseMapper.refreshAccountMoney(hourStr);
            //清空缓存
            accounts.forEach(groupAccount -> RedisUtils.deleteObject(OrderConstants.pay_group_account + groupAccount.getId()));
        }

    }
}
